package com.cy.utils.factors;


import com.cy.utils.jama.Matrix;

public class Factors {
//	public static final int MAX_STEPS = 20;
//	public static final int MAX_POWER = 2;
//	private double [] vFactors = new double [MAX_POWER+1];
//	private double[] vTs = new double [MAX_STEPS];
//	private double[] vNs = new double [MAX_STEPS];

	public static double power(double x, int p){
		if(p<=0)
			return 1;
		else
			return power(x, p-1)*x;
	}

	private static double average(double [] arr){
		if(arr.length ==0 )
			return 0;

		double s = 0;
		for(int i=0; i<arr.length; i++){
			s+=arr[i];
		}

		return s/arr.length;
	}

	/**
	 * Calculate factors d: d[0], d[1], ..., d[n], meets D = [1,T,T^2, ...,T^n ]*d
	 * D = d[n]*T^(n)+d[n-1]*T^(n-1)+...+d[0]*T^(0)
	 * where n >= 2
	 * @param vDs = D1....Dm, m sampled data in double array
	 * @param vTs = T1...Tm, m Temperature values in double array
	 * @return nD = d0, ..., dn, n factor values in int array
	 */
	public static int [] getDs(double [] vDs, double [] vTs, int power){
		int p = Math.min(3, power+1);

		if(vDs.length != vTs.length){
			System.out.println("vDs' dimension("+vDs.length+") is not equal to vTs' dimension("+vTs.length+").\n Calculation failed.");
			return null;
		}

//		int p1 = vDs.length+1;
//		double [] vDs1 = new double [p1];
//		double [] vTs1 = new double [p1];
//		for(int i=0; i<p1-1; i++){
//			vDs1[i] = vDs[i];
//			vTs1[i] = vTs[i];
//		}
//		vDs1[p1-1] = average(vDs);
//		vTs1[p1-1] = average(vTs);

		Matrix mD = new Matrix(vDs, vDs.length);
		double [][] aT = new double [vTs.length][p];
		for(int i=0; i<vTs.length; i++){
			//add 1% noise to vTs
//			double t = vTs1[i];//*(1+(Math.random()-0.5)/1000);
			for(int j=0; j<p; j++){
				aT[i][j] = Factors.power(vTs[i]/1E2, j);
			}
		}

		Matrix mT = new Matrix(aT);
		Matrix mF = mT.solve(mD);

		int [] vD = new int [p];
		for(int i=0; i<p; i++){
			vD[i] = (int) Math.round(mF.get(i, 0));
		}

		return vD;
	}


	/**
	 * Calculate factors t[0], t[1], ..., t[n], meets Temperature = [1,D,D^2, ...,D^n ]*n
	 * Temperature = t[n]*D^(n)+t[n-1]*D^(n-1)+...+t[0]
	 * @param vTs = T1...Tm, m Temperature values in double array
	 * @param vDs = D1....Dm, m sampled data values in double array
	 * @return ts = t0, ..., tn, n factor values in int array
	 */
	public static int [] getTs(double [] vTs, double [] vDs, int power){
		int p = Math.min(3, power+1);

		if(vDs.length != vTs.length){
			System.err.println("vDs' dimension("+vDs.length+") is not equal to vTs' dimension("+vTs.length+").\n Calculation failed.");
			return null;
		}

//		int p1 = vDs.length+1;
//		double [] vDs1 = new double [p1];
//		double [] vTs1 = new double [p1];
//		for(int i=0; i<p1-1; i++){
//			vDs1[i] = vDs[i];
//			vTs1[i] = vTs[i];
//		}
//		vDs1[p1-1] = average(vDs);
//		vTs1[p1-1] = average(vTs);

		Matrix mT = new Matrix(vTs, vTs.length);
		double [][] aD = new double [vDs.length][p];
		for(int i=0; i<vDs.length; i++){
			//add 1% noise to vDs
//			double d =vDs1[i];// +(1+(Math.random()-0.5)/500);
			for(int j=0; j<p; j++){
				aD[i][j] = Factors.power(vDs[i]/1E3, j);
			}
		}

		Matrix mD = new Matrix(aD);
		Matrix mG = mD.solve(mT);

		int [] vT = new int [p];
		for(int i=0; i<p; i++){
			vT[i] = (int)Math.round(mG.get(i, 0));
		}

		return vT;
	}

	/**
	 *
	 * @param vFr: double [] list of Pump level in 0...100 percent  每组设定的水泵功率
	 * @param vFs: double [] list of Flow level in 0...10000 uL/s   流速（经过KV重新校正）
	 * @return model ABC: Fr = (m[1]*Fs/1E3+m[0])*(10^m[2])/1E3; m[2] is checksum of m[0], m[1], m[2]
	 */
	public static int [] calcPumpLinearModel(double [] vFr, double [] vFs){
		int p = Math.min(vFs.length, vFr.length);
		int [] model = new int[4];

		//build A and B for A*x=B
		double [][] aA = new double [p][2];
		double [] vB = new double [p];
		for (int i=0; i< p; i++){
			vB[i] = vFr[i];
			aA[i][0] = 1;
			aA[i][1] = vFs[i]/1E3;
		}

		Matrix mA = new Matrix(aA);
		Matrix mB = new Matrix(vB, p);

		Matrix mX = mA.solve(mB);

		model[0] =  (int)(mX.get(0,0)*1E3);
		model[1] =  (int)(mX.get(1,0)*1E3);

		int m = Math.max(Math.abs(model[0]),Math.abs(model[1]));

		model[3] = 0;
		model[2] = 0;
		for(;m>32767;m/=10){
			model[0]/=10;
			model[1]/=10;
			model[2]++;
			m = Math.max(Math.abs(model[0]),Math.abs(model[1]));
		}

		//X
		model[3]+= model[0];

		//Y
		model[3]+=model[1];

		//Z
		model[3]+=model[2];

		return model;
	}


	/**
	 *
	 * @param vP: double [] list of Power level in 0...100 percent   加热功率
	 * @param vFs: double [] list of Flow level in 0...10000 uL/s    流速
	 * @param vDd: double [] list   出水温度平稳后锅炉的采样值 d值
	 * @param vDe: double [] list  出水温度平稳后水槽入水口采样值 d值
 	 * @param vt: double [] list vt为模型参数{t0, t1, t2}
	 * @return model: P = ((m[0]*(((Td-Te)*Fs)^2/1E5+1.2*£¨Td-Te)*Fs)/1E5+m[1])*10^m[2]/1E3; m[3] is checksum of m[0], m[1], m[2]  返回的值为{x, y, z, chekcsum}
	 */
	/**
	 *
	 * @param vP: double [] list of Power level in 0...100 percent  加热功率
	 * @param vFs: double [] list of Flow level in 0...10000 uL/s   流速
	 * @param vTd: double [] list of Td in 0...150 in °Ê   出水口温度
	 * @param vTe: double [] list of Te in 0...150 in °Ê
	 * @return model XYZ: P = ((m[0]*((Td-Te)*Fs/1E5+m[1])*(10^m[2])/1E3; m[3] is checksum of m[0], m[1], m[2]
	 */
	public static int [] calcDynamicXYZModel(double [] vP, double [] vFs, double [] vTd, double [] vTe){
		int p = Math.min(vP.length, Math.min(vFs.length, Math.min(vTd.length, vTe.length)));
		int [] model = new int[4];

		//build A and B for A*x=B
		double [][] aA = new double [p][2];
		double [] vB = new double [p];
		for (int i=0; i< p; i++){
			vB[i] = vP[i];
			aA[i][0] = (vTd[i]-vTe[i])*vFs[i]/1E5;
			aA[i][1] = 1;
		}

		Matrix mA = new Matrix(aA);
		Matrix mB = new Matrix(vB, p);

		Matrix mX = mA.solve(mB);

		model[0] =  (int)(mX.get(0,0)*1E3);
		model[1] =  (int)(mX.get(1,0)*1E3);

		int m = Math.max(Math.abs(model[0]),Math.abs(model[1]));

		model[3] = 0;
		model[2] = 0;
		for(;m>32767;m/=10){
			model[0]/=10;
			model[1]/=10;
			model[2]++;
			m = Math.max(Math.abs(model[0]),Math.abs(model[1]));
		}

		//X
		model[3]+= model[0];

		//Y
		model[3]+=model[1];

		//Z
		model[3]+=model[2];

		return model;
	}





	/**
	 *
	 * @param vP: double [] list of Power level in 0...100 percent
	 * @param vFs: double [] list of Flow level in 0...10000 uL/s
	 * @param vTo: double [] list of To in 0...150 in ¡æ
	 * @return model: P = m[0]*To*power(10,m[3])/1E4+m[1]*Fs*power(10,m[3])/1E6+m[2]*power(10,m[3])/1E2; m[4] is checksum of m[0], m[1], m[2], m[3]
	 */
	public static int [] calcDynamicABCDModel(double [] vP, double [] vFs, double [] vTo){
		int p = Math.min(vP.length, Math.min(vFs.length, vTo.length));
		int a_b_ratio = 40;
		int [] model = new int[5];

		//build A and B for A*x=B
		double [][] aA = new double [p][2];
		double [] vB = new double [p];
		for (int i=0; i< p; i++){
			vB[i] = vP[i]*(1+(Math.random()-0.5)*2*0.0001);
			aA[i][0] = (vTo[i]+vFs[i]/a_b_ratio)*(1+(Math.random()-0.5)*2*0.0001);
			aA[i][1] = 1;
		}

		Matrix mA = new Matrix(aA);
		Matrix mB = new Matrix(vB, p);

		Matrix mX = mA.solve(mB);

		model[0] =  (int)(mX.get(0,0)*1E4);
		model[1] =  (int)(mX.get(0,0)/a_b_ratio*1E6);
		model[2] =  (int)(mX.get(1,0)*1E2);

		int m = Math.max(Math.abs(model[0]), Math.max(Math.abs(model[1]), Math.abs(model[2])));

		model[3] = 0;
		model[4] = 0;
		for(;m>32767;m/=10){
			model[0]/=10;
			model[1]/=10;
			model[2]/=10;
			model[3]++;
			m = Math.max(Math.abs(model[0]), Math.max(Math.abs(model[1]), Math.abs(model[2])));
		}

		//a
		model[4]+= model[0];

		//b
		model[4]+=model[1];

		//c
		model[4]+=model[2];

		//d
		model[4]+=model[3];
		return model;
	}

	/**
	 *
	 * @param vP: double [] list of Power level in 0...100
	 * @param vT: double [] list of To in 0...150
	 * @return model: P = m[0]*T/10000+m[1]*F/10000+m[2]/100; m[3] is checksum of m[0], m[1], m[2]. m[1] is zero forever.
	 */
	public static int [] calcDynamicACModel(double [] vP, double [] vT){
		int p = Math.min(vP.length, vT.length);
		int [] model = new int[4];

		//build A and B for A*x=B
		double [][] aA = new double [p][2];
		double [] vB = new double [p];
		for (int i=0; i< p; i++){
			vB[i] = 1;
			aA[i][0] = vP[i];
			aA[i][1] = vT[i];
		}

		Matrix mA = new Matrix(aA);
		Matrix mB = new Matrix(vB, p);

		Matrix mX = mA.solve(mB);

		double d = mX.get(0, 0);
		d = (Math.abs(d)<(10^-5))?(10^-5):d;

		model[3] = 0;
		//a
		model[3]+=model[0] = (int)(-mX.get(1,0)/d*10000);
		//b..zero forever.
		model[3]+=model[1] = 0;
		//c
		model[3]+=model[2] = (int)(1/d*100);

		return model;

	}

	/**
	 *
	 * @param vVector, array to calculate check sum.
	 * @return check sum in 0xFFFF
	 */
	public static int checksum(int[]vVector){
		if(vVector==null || vVector.length==0)
			return 0;

		int cs = 0;
		for(int i=0;i<vVector.length;i++)
			cs+=vVector[i];

		return cs%65536;
	}

	/**
	 * Calculate D's standard deviation. D=d*T
	 * @param vDs
	 * @param vTs
	 * @param vD = d
	 * @return
	 */
	public static double getDDevRatio(double [] vDs, double [] vTs, int [] vD){
		if (vTs==null || vDs ==null || vD == null)
		{
			System.err.println("Parameters can not be null. Calculation failed.");
			return -1;
		}

		if (vTs.length != vDs.length){
			System.err.println("vDs' dimension("+vDs.length+") is not equal to vTs' dimension("+vTs.length+").\n Calculation failed.");
			return -1;
		}

		int p = vD.length;
		double d = 0;
		for(int i=0; i<vDs.length; i++){
			double temp = 0;
			for (int j=p-1; j>=0; j--){
				temp = temp*vTs[i]/1E2+vD[j];
			}
			temp/=vDs[i];
			temp--;
			d += temp*temp;
		}

		d/=vTs.length;
		return Math.sqrt(d);
	}


	/**
	 * Calculate T's standard deviation. T=t*D
	 * @param vTs
	 * @param vDs
	 * @param vT = t
	 * @return
	 */
	public static double getTDevRatio(double [] vTs, double [] vDs, int [] vT){
		if (vTs==null || vDs ==null || vT == null)
		{
			System.err.println("Parameters can not be null. Calculation failed.");
			return -1;
		}

		if (vTs.length != vDs.length){
			System.err.println("vDs' dimension("+vDs.length+") is not equal to vTs' dimension("+vTs.length+").\n Calculation failed.");
			return -1;
		}

		int p = vT.length;
		double d = 0;
		for(int i=0; i<vTs.length; i++){
			double temp = 0;
			for (int j=p-1; j>=0; j--){
				temp = temp*vDs[i]/1E3+vT[j];
			}
			temp/=vTs[i];
			temp--;

			d += temp*temp;
		}

		d/=vTs.length;
		return Math.sqrt(d);
	}




	public static String toHex2Bytes(int value){
		String str = String.format("%04X",value).toUpperCase();
		int start = str.length()-4;
		str = str.substring(start);
		return str;
	}



}

